/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
package com.facebook.imagepipeline.bitmaps

import com.facebook.common.memory.PooledByteBuffer
import com.facebook.common.memory.PooledByteBufferFactory
import com.facebook.common.memory.PooledByteBufferOutputStream
import com.facebook.common.references.CloseableReference
import java.io.IOException

/**
 * Producer that emits a dummy, fixed-size JPEG bytearray.
 *
 * The purpose of this jpeg is to serve as the source for a purgeable bitmap which will in turn have
 * its contents overwritten, enabling us to copy into a purgeable bitmap.
 */
class EmptyJpegGenerator(private val mPooledByteBufferFactory: PooledByteBufferFactory) {
  fun generate(width: Short, height: Short): CloseableReference<PooledByteBuffer?> {
    var os: PooledByteBufferOutputStream? = null
    try {
      os =
          mPooledByteBufferFactory.newOutputStream(
              EMPTY_JPEG_PREFIX.size + EMPTY_JPEG_SUFFIX.size + 4
          )
      os.write(EMPTY_JPEG_PREFIX)
      os.write((height.toInt() shr 8).toByte().toInt())
      os.write((height.toInt() and 0x00ff).toByte().toInt())
      os.write((width.toInt() shr 8).toByte().toInt())
      os.write((width.toInt() and 0x00ff).toByte().toInt())
      os.write(EMPTY_JPEG_SUFFIX)
      return CloseableReference.of<PooledByteBuffer?>(os.toByteBuffer())
    } catch (e: IOException) {
      throw RuntimeException(e)
    } finally {
      os?.close()
    }
  }

  companion object {
    // The following JPEG was generated by compressing a 1x1 bitmap on an Android device
    // and logcatting the output.
    private val EMPTY_JPEG_PREFIX =
        byteArrayOf(
            0xff.toByte(),
            0xd8.toByte(), // Start of Image marker
            // Quantization table

            0xff.toByte(),
            0xdb.toByte(), // DQT marker
            0x00.toByte(),
            0x43.toByte(), // Length
            0x00.toByte(), // Precision: 0 Id: 0
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(),
            0xff.toByte(), // Frame data
            0xff.toByte(),
            0xc0.toByte(), // SOF marker
            0x00.toByte(),
            0x11.toByte(), // Frame length
            0x08.toByte(), // Sample precision
        )

    // Width and height (16 bits each) are inserted between these two
    private val EMPTY_JPEG_SUFFIX =
        byteArrayOf(
            0x03.toByte(), // Number of components
            0x01.toByte(), // Component 1
            0x22.toByte(), // Sampling factor
            0x00.toByte(), // Quantization selector
            0x02.toByte(), // Component 2
            0x11.toByte(), // Sampling factor
            0x00.toByte(), // Quantization selector
            0x03.toByte(), // Component 3
            0x11.toByte(), // Sampling factor
            0x00.toByte(), // Quantization selector
            // Huffman table

            0xff.toByte(),
            0xc4.toByte(), // DHT marker
            0x00.toByte(),
            0x1f.toByte(), // Length
            0x00.toByte(), // Table class: DC id: 0
            // Huffman code lengths
            0x00.toByte(),
            0x01.toByte(),
            0x05.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(), // Huffman code values
            0x00.toByte(),
            0x01.toByte(),
            0x02.toByte(),
            0x03.toByte(),
            0x04.toByte(),
            0x05.toByte(),
            0x06.toByte(),
            0x07.toByte(),
            0x08.toByte(),
            0x09.toByte(),
            0x0a.toByte(),
            0x0b.toByte(),
            0xff.toByte(),
            0xc4.toByte(),
            0x00.toByte(),
            0xb5.toByte(),
            0x10.toByte(),
            0x00.toByte(),
            0x02.toByte(),
            0x01.toByte(),
            0x03.toByte(),
            0x03.toByte(),
            0x02.toByte(),
            0x04.toByte(),
            0x03.toByte(),
            0x05.toByte(),
            0x05.toByte(),
            0x04.toByte(),
            0x04.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x01.toByte(),
            0x7d.toByte(),
            0x01.toByte(),
            0x02.toByte(),
            0x03.toByte(),
            0x00.toByte(),
            0x04.toByte(),
            0x11.toByte(),
            0x05.toByte(),
            0x12.toByte(),
            0x21.toByte(),
            0x31.toByte(),
            0x41.toByte(),
            0x06.toByte(),
            0x13.toByte(),
            0x51.toByte(),
            0x61.toByte(),
            0x07.toByte(),
            0x22.toByte(),
            0x71.toByte(),
            0x14.toByte(),
            0x32.toByte(),
            0x81.toByte(),
            0x91.toByte(),
            0xa1.toByte(),
            0x08.toByte(),
            0x23.toByte(),
            0x42.toByte(),
            0xb1.toByte(),
            0xc1.toByte(),
            0x15.toByte(),
            0x52.toByte(),
            0xd1.toByte(),
            0xf0.toByte(),
            0x24.toByte(),
            0x33.toByte(),
            0x62.toByte(),
            0x72.toByte(),
            0x82.toByte(),
            0x09.toByte(),
            0x0a.toByte(),
            0x16.toByte(),
            0x17.toByte(),
            0x18.toByte(),
            0x19.toByte(),
            0x1a.toByte(),
            0x25.toByte(),
            0x26.toByte(),
            0x27.toByte(),
            0x28.toByte(),
            0x29.toByte(),
            0x2a.toByte(),
            0x34.toByte(),
            0x35.toByte(),
            0x36.toByte(),
            0x37.toByte(),
            0x38.toByte(),
            0x39.toByte(),
            0x3a.toByte(),
            0x43.toByte(),
            0x44.toByte(),
            0x45.toByte(),
            0x46.toByte(),
            0x47.toByte(),
            0x48.toByte(),
            0x49.toByte(),
            0x4a.toByte(),
            0x53.toByte(),
            0x54.toByte(),
            0x55.toByte(),
            0x56.toByte(),
            0x57.toByte(),
            0x58.toByte(),
            0x59.toByte(),
            0x5a.toByte(),
            0x63.toByte(),
            0x64.toByte(),
            0x65.toByte(),
            0x66.toByte(),
            0x67.toByte(),
            0x68.toByte(),
            0x69.toByte(),
            0x6a.toByte(),
            0x73.toByte(),
            0x74.toByte(),
            0x75.toByte(),
            0x76.toByte(),
            0x77.toByte(),
            0x78.toByte(),
            0x79.toByte(),
            0x7a.toByte(),
            0x83.toByte(),
            0x84.toByte(),
            0x85.toByte(),
            0x86.toByte(),
            0x87.toByte(),
            0x88.toByte(),
            0x89.toByte(),
            0x8a.toByte(),
            0x92.toByte(),
            0x93.toByte(),
            0x94.toByte(),
            0x95.toByte(),
            0x96.toByte(),
            0x97.toByte(),
            0x98.toByte(),
            0x99.toByte(),
            0x9a.toByte(),
            0xa2.toByte(),
            0xa3.toByte(),
            0xa4.toByte(),
            0xa5.toByte(),
            0xa6.toByte(),
            0xa7.toByte(),
            0xa8.toByte(),
            0xa9.toByte(),
            0xaa.toByte(),
            0xb2.toByte(),
            0xb3.toByte(),
            0xb4.toByte(),
            0xb5.toByte(),
            0xb6.toByte(),
            0xb7.toByte(),
            0xb8.toByte(),
            0xb9.toByte(),
            0xba.toByte(),
            0xc2.toByte(),
            0xc3.toByte(),
            0xc4.toByte(),
            0xc5.toByte(),
            0xc6.toByte(),
            0xc7.toByte(),
            0xc8.toByte(),
            0xc9.toByte(),
            0xca.toByte(),
            0xd2.toByte(),
            0xd3.toByte(),
            0xd4.toByte(),
            0xd5.toByte(),
            0xd6.toByte(),
            0xd7.toByte(),
            0xd8.toByte(),
            0xd9.toByte(),
            0xda.toByte(),
            0xe1.toByte(),
            0xe2.toByte(),
            0xe3.toByte(),
            0xe4.toByte(),
            0xe5.toByte(),
            0xe6.toByte(),
            0xe7.toByte(),
            0xe8.toByte(),
            0xe9.toByte(),
            0xea.toByte(),
            0xf1.toByte(),
            0xf2.toByte(),
            0xf3.toByte(),
            0xf4.toByte(),
            0xf5.toByte(),
            0xf6.toByte(),
            0xf7.toByte(),
            0xf8.toByte(),
            0xf9.toByte(),
            0xfa.toByte(),
            0xff.toByte(),
            0xc4.toByte(),
            0x00.toByte(),
            0x1f.toByte(),
            0x01.toByte(),
            0x00.toByte(),
            0x03.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x01.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x01.toByte(),
            0x02.toByte(),
            0x03.toByte(),
            0x04.toByte(),
            0x05.toByte(),
            0x06.toByte(),
            0x07.toByte(),
            0x08.toByte(),
            0x09.toByte(),
            0x0a.toByte(),
            0x0b.toByte(),
            0xff.toByte(),
            0xc4.toByte(), // DHT marker
            0x00.toByte(),
            0xb5.toByte(), // Length
            0x11.toByte(), // Table class: AC id: 1
            // Huffman code lengths
            0x00.toByte(),
            0x02.toByte(),
            0x01.toByte(),
            0x02.toByte(),
            0x04.toByte(),
            0x04.toByte(),
            0x03.toByte(),
            0x04.toByte(),
            0x07.toByte(),
            0x05.toByte(),
            0x04.toByte(),
            0x04.toByte(),
            0x00.toByte(),
            0x01.toByte(),
            0x02.toByte(),
            0x77.toByte(), // Huffman code values
            0x00.toByte(),
            0x01.toByte(),
            0x02.toByte(),
            0x03.toByte(),
            0x11.toByte(),
            0x04.toByte(),
            0x05.toByte(),
            0x21.toByte(),
            0x31.toByte(),
            0x06.toByte(),
            0x12.toByte(),
            0x41.toByte(),
            0x51.toByte(),
            0x07.toByte(),
            0x61.toByte(),
            0x71.toByte(),
            0x13.toByte(),
            0x22.toByte(),
            0x32.toByte(),
            0x81.toByte(),
            0x08.toByte(),
            0x14.toByte(),
            0x42.toByte(),
            0x91.toByte(),
            0xa1.toByte(),
            0xb1.toByte(),
            0xc1.toByte(),
            0x09.toByte(),
            0x23.toByte(),
            0x33.toByte(),
            0x52.toByte(),
            0xf0.toByte(),
            0x15.toByte(),
            0x62.toByte(),
            0x72.toByte(),
            0xd1.toByte(),
            0x0a.toByte(),
            0x16.toByte(),
            0x24.toByte(),
            0x34.toByte(),
            0xe1.toByte(),
            0x25.toByte(),
            0xf1.toByte(),
            0x17.toByte(),
            0x18.toByte(),
            0x19.toByte(),
            0x1a.toByte(),
            0x26.toByte(),
            0x27.toByte(),
            0x28.toByte(),
            0x29.toByte(),
            0x2a.toByte(),
            0x35.toByte(),
            0x36.toByte(),
            0x37.toByte(),
            0x38.toByte(),
            0x39.toByte(),
            0x3a.toByte(),
            0x43.toByte(),
            0x44.toByte(),
            0x45.toByte(),
            0x46.toByte(),
            0x47.toByte(),
            0x48.toByte(),
            0x49.toByte(),
            0x4a.toByte(),
            0x53.toByte(),
            0x54.toByte(),
            0x55.toByte(),
            0x56.toByte(),
            0x57.toByte(),
            0x58.toByte(),
            0x59.toByte(),
            0x5a.toByte(),
            0x63.toByte(),
            0x64.toByte(),
            0x65.toByte(),
            0x66.toByte(),
            0x67.toByte(),
            0x68.toByte(),
            0x69.toByte(),
            0x6a.toByte(),
            0x73.toByte(),
            0x74.toByte(),
            0x75.toByte(),
            0x76.toByte(),
            0x77.toByte(),
            0x78.toByte(),
            0x79.toByte(),
            0x7a.toByte(),
            0x82.toByte(),
            0x83.toByte(),
            0x84.toByte(),
            0x85.toByte(),
            0x86.toByte(),
            0x87.toByte(),
            0x88.toByte(),
            0x89.toByte(),
            0x8a.toByte(),
            0x92.toByte(),
            0x93.toByte(),
            0x94.toByte(),
            0x95.toByte(),
            0x96.toByte(),
            0x97.toByte(),
            0x98.toByte(),
            0x99.toByte(),
            0x9a.toByte(),
            0xa2.toByte(),
            0xa3.toByte(),
            0xa4.toByte(),
            0xa5.toByte(),
            0xa6.toByte(),
            0xa7.toByte(),
            0xa8.toByte(),
            0xa9.toByte(),
            0xaa.toByte(),
            0xb2.toByte(),
            0xb3.toByte(),
            0xb4.toByte(),
            0xb5.toByte(),
            0xb6.toByte(),
            0xb7.toByte(),
            0xb8.toByte(),
            0xb9.toByte(),
            0xba.toByte(),
            0xc2.toByte(),
            0xc3.toByte(),
            0xc4.toByte(),
            0xc5.toByte(),
            0xc6.toByte(),
            0xc7.toByte(),
            0xc8.toByte(),
            0xc9.toByte(),
            0xca.toByte(),
            0xd2.toByte(),
            0xd3.toByte(),
            0xd4.toByte(),
            0xd5.toByte(),
            0xd6.toByte(),
            0xd7.toByte(),
            0xd8.toByte(),
            0xd9.toByte(),
            0xda.toByte(),
            0xe2.toByte(),
            0xe3.toByte(),
            0xe4.toByte(),
            0xe5.toByte(),
            0xe6.toByte(),
            0xe7.toByte(),
            0xe8.toByte(),
            0xe9.toByte(),
            0xea.toByte(),
            0xf2.toByte(),
            0xf3.toByte(),
            0xf4.toByte(),
            0xf5.toByte(),
            0xf6.toByte(),
            0xf7.toByte(),
            0xf8.toByte(),
            0xf9.toByte(),
            0xfa.toByte(), // Scan data
            0xff.toByte(),
            0xda.toByte(), // Start of Scan marker
            0x00.toByte(),
            0x0c.toByte(), // Scan header length
            0x03.toByte(), // Number of components
            0x01.toByte(), // Component 1
            0x00.toByte(), // Coding table selector
            0x02.toByte(), // Start spectral selectin
            0x11.toByte(), // End spectral selection
            0x03.toByte(), // Approximation bits
            // Huffman-encoded data

            0x11.toByte(),
            0x00.toByte(),
            0x3f.toByte(),
            0x00.toByte(),
            0x8e.toByte(),
            0x8a.toByte(),
            0x28.toByte(),
            0xa0.toByte(),
            0x0f.toByte(), // End of Image
            0xff.toByte(),
            0xd9.toByte(), // EOI marker
        )
  }
}
